﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.ADF;
//using ESRI.ArcGIS.ADF.Connection.Local;
using System.Reflection;
using PlanningGIS.Framework;

using System.Runtime.InteropServices;
using System.Threading;
using System.IO;
using System.Xml;
using PlanningGIS.ArcGIS;
using PlanningGIS.Util.Log;

namespace PlanningGIS.CoordCovert.model
{
    
    public class GeosocConvert3 : Operator2Ex
    {

        private List<IFeatureClass> pFeatClses;
        private string dbPath = "";
        double resolution, upleftx, uplefty;
        int rate, row, col;
        byte[] detas;
        private object missing = Type.Missing;
        
        /// <summary>
        /// 需要进行转换的要素类集合
        /// </summary>
        public List<IFeatureClass> FeatClses
        {
            set { pFeatClses = value; }
        }

        public string DbPath
        {
            set { dbPath = value; }
        }

        public GeosocConvert3(string path)
        {
            dbPath = path;

            if (!File.Exists(dbPath)) return;

            BinaryReader binReader = new BinaryReader(File.Open(dbPath, FileMode.Open));
            byte[] buffer = binReader.ReadBytes(16);

            if (System.Text.Encoding.Default.GetString(buffer).Trim('\0') != "PlanningGIS-TranDB") return;

            buffer = binReader.ReadBytes(16);
            resolution = Double.Parse(System.Text.Encoding.Default.GetString(buffer));

            buffer = binReader.ReadBytes(16);
            rate = Int32.Parse(System.Text.Encoding.Default.GetString(buffer));

            buffer = binReader.ReadBytes(16);
            row = Int32.Parse(System.Text.Encoding.Default.GetString(buffer));

            buffer = binReader.ReadBytes(16);
            col = Int32.Parse(System.Text.Encoding.Default.GetString(buffer));

            buffer = binReader.ReadBytes(16);
            upleftx = Double.Parse(System.Text.Encoding.Default.GetString(buffer));

            buffer = binReader.ReadBytes(16);
            uplefty = Double.Parse(System.Text.Encoding.Default.GetString(buffer));

            buffer = binReader.ReadBytes(16 * 9);

            detas = binReader.ReadBytes(row * col * 8);

            binReader.Close();
        }


        /// <summary>
        /// 数据转换
        /// </summary>
        /// <returns></returns>
        public override bool Operate()
        {
            if (pFeatClses == null || pFeatClses.Count == 0)
            {
                LogHelper.WriteLog("请设置需要进行变形的数据！", zjgisLogType.zjgisLogError);
                return false;
            }

            if (dbPath == null)
            {
                LogHelper.WriteLog("请选择二进制数据文件！", zjgisLogType.zjgisLogError);
                return false;
            }


            setProcessBarMax.Invoke(pFeatClses.Count);

            int countTotal = 0;
            int errorCountTotal = 0;
            int rightCountTotal = 0;

            try
            {

                for (int i = 0; i < pFeatClses.Count; i++)
                {
                    if (stopAnyway)
                        return false;

                    setProcessBarCurrent.Invoke(i + 1);

                    IFeatureClass pFeatCls = pFeatClses[i];

                    int allCount = pFeatCls.FeatureCount(null);
                    if (allCount == 0) continue;

                    setSubProcessBarMax(allCount);

                    LogHelper.WriteLog("开始对数据[" + pFeatCls.AliasName + "]进行变形转换...");

                    #region 逐要素转换

                    IFeatureCursor pFeatCur = pFeatCls.Update(null, false);
                    IFeature pFeature = pFeatCur.NextFeature();

                    int count = 0;
                    int rightCount = 0;
                    int errorCount = 0;

                    while (pFeature != null)
                    {
                        if (stopAnyway)
                            return false;

                        count++;
                        SetSubProcessBarCurrent.Invoke(count);


                        if (ConvertFeature(pFeature))
                        {
                            rightCount++;
                        }
                        else
                        {
                            errorCount++;
                        }
                        pFeatCur.UpdateFeature(pFeature);
                        pFeature = pFeatCur.NextFeature();
                    }

                    if (errorCount == 0)
                    {
                        LogHelper.WriteLog("[" + pFeatCls.AliasName + "]数据转换完毕。待转换总数：" + count + "；成功：" + rightCount + "；失败：" + errorCount + "\n", zjgisLogType.zjgisLogDone);
                    }
                    else
                    {
                        LogHelper.WriteLog("[" + pFeatCls.AliasName + "]数据转换完毕。待转换总数：" + count + "；成功：" + rightCount + "；失败：" + errorCount + "\n", zjgisLogType.zjgisLogError);
                    }

                    countTotal += count;
                    rightCountTotal += rightCount;
                    errorCountTotal += errorCount;

                    Marshal.ReleaseComObject(pFeatCur);

                    #endregion

                    //AddFeatureclassSpatialIndex(pFeatCls, 0, 0, 0);
                }

                if (errorCountTotal == 0)
                {
                    LogHelper.WriteDoneLog("所有数据转换完毕。待转换总数：" + countTotal + "；成功：" + rightCountTotal + "；失败：" + errorCountTotal + "\n");
                }
                else
                {
                   LogHelper.WriteErrLog("所有数据转换完毕。待转换总数：" + countTotal + "；成功：" + rightCountTotal + "；失败：" + errorCountTotal + "\n");
                }

                return true;
            }
            catch (Exception ex)
            {
               LogHelper.WriteErrLog("转换失败：" + ex.Message);
                return false;
            }
        }

        #region private functions

        /// <summary>
        /// 对单个要素进行转换
        /// </summary>
        /// <param name="pFeature"></param>
        /// <returns>转换成功返回true、否则返回false</returns>
        private bool ConvertFeature(IFeature pFeature)
        {
            IGeometry pGeometry = pFeature.ShapeCopy;
            if (pGeometry.IsEmpty) return true;     //如果图形为空，则无需转换

            bool suc = true;

            try
            {
                if (pGeometry.GeometryType == esriGeometryType.esriGeometryMultipoint ||
                    pGeometry.GeometryType == esriGeometryType.esriGeometryPolyline)
                {
                    IPointCollection pPoints = pGeometry as IPointCollection;
                    for (int i = 0; i < pPoints.PointCount; i++)
                    {
                        IPoint pPoint = pPoints.get_Point(i);

                        suc = ConvertPoint(pPoint);
                        if (!suc) break;
                        pPoints.UpdatePoint(i, pPoint);
                    }

                    
                }
                //如果是面状数据，因为首尾点是同一个点，第一个点做了坐标转换后，最后一个点是不需要再进行转换的，否则该点（首/尾点）就会转换了两次
                else if (pGeometry.GeometryType == esriGeometryType.esriGeometryPolygon)
                {
                    #region 可以较好的转换面图层，但是Z值信息会丢失，转换后全为零
                    IGeometryCollection pGeometryNew = new GeometryBagClass();
                    IPolygon pPolygon = null;
                    ITopologicalOperator2 pTop = null;

                    IGeometryCollection pGeometries = pGeometry as IGeometryCollection;
                    if (pGeometries == null || pGeometries.GeometryCount == 0) return true;

                    bool hasZ = false;
                    IFeatureClass pFeatCls = (pFeature.Class as IFeatureClass);
                    IField pField = pFeatCls.Fields.get_Field(pFeatCls.FindField(pFeatCls.ShapeFieldName));
                    IFieldEdit pFieldEdit = pField as IFieldEdit;
                    IGeometryDef pGeometryDef = pFieldEdit.GeometryDef;
                    hasZ = pGeometryDef.HasZ;

                    for (int i = 0; i < pGeometries.GeometryCount; i++)
                    {
                        if (!suc) break;

                        IGeometry pGeomLow = pGeometries.get_Geometry(i);
                        IGeometry pGeomHigh = Convertor.ConvertLow2High(pGeomLow);

                        if (hasZ)
                        {
                            pGeomHigh = GeometryHelper.Geom2Dto3D(pGeomHigh);
                        }

                        IPointCollection pPoints = pGeomHigh as IPointCollection;

                        //最后一个点不用转！
                        for (int j = 0; j < pPoints.PointCount - 1; j++)
                        {
                            IPoint pPoint = pPoints.get_Point(j);

                            suc = ConvertPoint(pPoint);

                            if (!suc) break;

                            pPoints.UpdatePoint(j, pPoint);
                        }

                        if (pPolygon == null)
                        {
                            pPolygon = pGeomHigh as IPolygon;
                            pTop = pPolygon as ITopologicalOperator2;
                        }
                        else
                        {
                            pTop.IsKnownSimple_2 = false;
                            if (!pTop.IsKnownSimple) pTop.Simplify();

                            ITopologicalOperator2 pTopTemp = pGeomHigh as ITopologicalOperator2;
                            pTopTemp.IsKnownSimple_2 = false;
                            if (!pTopTemp.IsKnownSimple) pTopTemp.Simplify();

                            pPolygon = pTop.Union(pGeomHigh) as IPolygon;
                            pTop = pPolygon as ITopologicalOperator2;
                        }
                    }

                    pFeature.Shape = pPolygon;
                    #endregion
                }
                else if (pGeometry.GeometryType == esriGeometryType.esriGeometryPoint)
                {
                    IPoint pPoint = pGeometry as IPoint;

                    suc = ConvertPoint(pPoint);
                }
                else
                {
                   LogHelper.WriteErrLog("类型不支持！");
                    return false;
                }

                if (!suc)
                {
                   LogHelper.WriteErrLog("ID为[" + pFeature.OID.ToString() + "]的要素转超出了格网控制点的范围，无法进行变形！");
                    return false;

                }

                pFeature.Shape = pGeometry;
                //pFeature.Store();

                return true;
            }
            catch (Exception ex)
            {
               LogHelper.WriteErrLog("对ID为[" + pFeature.OID.ToString() + "]的要素转换失败：" + ex.Message);
                return false;
            }
        }

        public bool GetDeta(double x, double y, out double detax, out double detay)
        {
            int i = (int)Math.Round((uplefty - y) / resolution, 0);
            int j = (int)Math.Round((x - upleftx) / resolution, 0);

            detax = 0;
            detay = 0;

            //超过控制点范围的
            if (i < 0 || i >= row) return false;
            if (j < 0 || j >= col) return false;

            int start = (i * col + j) * 8;

            int r1 = detas[start + 0];
            int r2 = detas[start + 1];
            int r3 = detas[start + 2];
            int r4 = detas[start + 3];


            detax = (r1 + r2 * 256 + r3 * 256 * 256 + r4 * 256 * 256 * 256) * 1.0 / rate;

            r1 = detas[start + 4];
            r2 = detas[start + 5];
            r3 = detas[start + 6];
            r4 = detas[start + 7];

            detay = (r1 + r2 * 256 + r3 * 256 * 256 + r4 * 256 * 256 * 256) * 1.0 / rate;

            return true;
        }

        /// <summary>
        /// 对某个点进行转换,纵方向取平均；横方向取加权
        /// </summary>
        /// <param name="pPoint"></param>
        private bool ConvertPoint(IPoint pPoint)
        {

            if (detas == null || detas.Length == 0) return false;

            double x = pPoint.X;
            double y = pPoint.Y;

            double tempx = ((int)(x / resolution)) * resolution;
            double tempy = ((int)(y / resolution)) * resolution;

            tempx = Math.Round(tempx, 8);
            tempy = Math.Round(tempy, 8);


            double L1 = x - tempx;
            double L2 = resolution - L1;

            double dx1, dy1;
            if (!GetDeta(tempx, tempy, out dx1, out dy1)) return false;

            double dxtmp, dytmp;
            if (!GetDeta(tempx, tempy + resolution, out dxtmp, out dytmp)) return false;

            dx1 = (dx1 + dxtmp) / 2;
            dy1 = (dy1 + dytmp) / 2;

            double dx2, dy2;
            if (!GetDeta(tempx + resolution, tempy + resolution, out dx2, out dy2)) return false;
            if (!GetDeta(tempx + resolution, tempy, out dxtmp, out dytmp)) return false;

            dx2 = (dx2 + dxtmp) / 2;
            dy2 = (dy2 + dytmp) / 2;

            double X = x + (L1 * dx2 + L2 * dx1) / resolution;
            double Y = y + (L1 * dy2 + L2 * dy1) / resolution;

            pPoint.X = X;
            pPoint.Y = Y;


            return true;

        }



        #endregion
    }
}
